<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>
<body>
  <div><h1 id="-express-">一个最简单的 express 应用</h1>
  <h2 id="labdoc-header-0">一、实验介绍</h2>
  <h3 id="labdoc-header-1">1.1 特别鸣谢</h3>
  <p>本课程来自 alsotang 制作的教程《Node.js 包教不包会》，感谢作者授权实验楼基于该教程制作实验课程，作者主页及课程项目地址如下：</p><ul>
  <li>作者主页：<a href="http://zhuanlan.zhihu.com/alsotang" target="_blank">http://zhuanlan.zhihu.com/alsotang</a></li>
  <li>课程代码：<a href="https://github.com/alsotang/node-lessons" target="_blank">https://github.com/alsotang/node-lessons</a></li>
  </ul>
  <p>特别推荐 <a href="https://cnodejs.org" target="_blank">CNode社区</a> - Node.js专业中文社区。</p><h3 id="labdoc-header-2">1.2 实验内容</h3>
  <p>建立一个 lesson1 项目，在其中编写代码。当在浏览器中访问 <code>http://localhost:3000/</code> 时，输出 <code>Hello World</code>。</p><h3 id="labdoc-header-3">1.3 实验知识点</h3>
  <ol>
  <li>包管理器 npm 。使用 npm 安装包，并自动安装所需依赖。</li>
  <li>框架 express 。学习新建 express 实例，并定义 routes ，产生输出。</li>
  </ol>
  <h3 id="labdoc-header-4">1.4 实验环境</h3>
  <ul>
  <li>Node.js v4.2.1</li>
  </ul>
  <h2 id="labdoc-header-5">二、实验步骤</h2>
  <h3 id="labdoc-header-6">2.1 包管理器 npm</h3>
  <p>npm 可以自动管理包的依赖. 只需要安装你想要的包, 不必考虑这个包的依赖包.</p><p>在 PHP 中, 包管理使用的 <code>Composer</code>, python 中，包管理使用 <code>easy_install</code> 或者 <code>pip</code>，ruby 中我们使用 <code>gem</code>。而在 Node.js 中，对应就是 <code>npm</code>，npm 是 <code>Node.js Package Manager</code> 的意思。</p><h3 id="labdoc-header-7">2.2 框架 Express</h3>
  <p>express 是 Node.js 应用最广泛的 web 框架，现在是 4.x 版本(注:截至2017.3.31，Node.js已更新至6.x版本)，它非常薄。跟 Rails 比起来，完全是两个极端。</p><p>express 的官网是 <a href="http://expressjs.com/" target="_blank">http://expressjs.com/</a> ，我常常上去看它的 API。</p><p>首先我们需要得到一个 express。</p><p>不同于 ruby 的 gem 装在全局，Node.js 的依赖是以项目为单位管理的，直接就安装在项目的 <code>node_modules</code> 目录下，而且每个依赖都可以有指定版本的其他依赖，这些依赖像一棵树一样。根据我自己的使用经验来说，npm 的体验在 pip 和 gem 之上。</p><p>OK，新建一个文件夹叫 lesson1 的，进去里面安装 express</p><pre><code class="hljs ruby"><span class="hljs-meta">$</span><span class="bash"> mkdir lesson1 &amp;&amp; <span class="hljs-built_in">cd</span> lesson1</span>
  <span class="hljs-meta">$</span><span class="bash"> npm install express</span>
  </code></pre><p>安装完成后，我们的 lesson1 目录下应该会出现一个 <code>node_modules</code> 文件夹，<code>ls</code> 看看</p><pre><code class="hljs ruby"><span class="hljs-meta">$</span><span class="bash"> ls node_modules</span>
  </code></pre><p>里面如果出现 express 文件夹则说明安装成功。</p><p>或者 npm命令提供更清晰直观的显示:</p><pre><code class="hljs coffeescript"><span class="hljs-meta">$</span><span class="bash"> <span class="hljs-built_in">npm</span> list</span>
  </code></pre><p>我们继续应用程序的编写。</p><p>新建一个 app.js 文件</p><pre><code class="hljs ruby"><span class="hljs-meta">$</span><span class="bash"> touch app.js</span>
  </code></pre><p>使用侧边栏剪贴板copy 进去这些代码，注意实验楼的剪贴板不支持中文，拷贝时候去掉注释：</p><pre><code class="hljs js javascript"><span class="hljs-comment"><span class="hljs-comment">// 这句的意思就是引入 `express` 模块，并将它赋予 `express` 这个变量等待使用。</span></span>
  <span class="hljs-keyword"><span class="hljs-keyword">var</span></span> express = <span class="hljs-built_in"><span class="hljs-built_in">require</span></span>(<span class="hljs-string"><span class="hljs-string">'express'</span></span>);
  <span class="hljs-comment"><span class="hljs-comment">// 调用 express 实例，它是一个函数，不带参数调用时，会返回一个 express 实例，将这个变量赋予 app 变量。</span></span>
  <span class="hljs-keyword"><span class="hljs-keyword">var</span></span> app = express();

  <span class="hljs-comment"><span class="hljs-comment">// app 本身有很多方法，其中包括最常用的 get、post、put/patch、delete，在这里我们调用其中的 get 方法，为我们的 `/` 路径指定一个 handler 函数。</span></span>
  <span class="hljs-comment"><span class="hljs-comment">// 这个 handler 函数会接收 req 和 res 两个对象，他们分别是请求的 request 和 response。</span></span>
  <span class="hljs-comment"><span class="hljs-comment">// request 中包含了浏览器传来的各种信息，比如 query 啊，body 啊，headers 啊之类的，都可以通过 req 对象访问到。</span></span>
  <span class="hljs-comment"><span class="hljs-comment">// res 对象，我们一般不从里面取信息，而是通过它来定制我们向浏览器输出的信息，比如 header 信息，比如想要向浏览器输出的内容。这里我们调用了它的 #send 方法，向浏览器输出一个字符串。</span></span>
  app.get(<span class="hljs-string"><span class="hljs-string">'/'</span></span>, <span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword">function</span></span></span><span class="hljs-function"> (</span><span class="hljs-params"><span class="hljs-function"><span class="hljs-params">req, res</span></span></span><span class="hljs-function">) </span></span>{
    res.send(<span class="hljs-string"><span class="hljs-string">'Hello World'</span></span>);
  });

  <span class="hljs-comment"><span class="hljs-comment">// 定义好我们 app 的行为之后，让它监听本地的 3000 端口。这里的第二个函数是个回调函数，会在 listen 动作成功后执行，我们这里执行了一个命令行输出操作，告诉我们监听动作已完成。</span></span>
  app.listen(<span class="hljs-number"><span class="hljs-number">3000</span></span>, <span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword">function</span></span></span><span class="hljs-function"> (</span><span class="hljs-params"></span><span class="hljs-function"><span class="hljs-params"></span>) </span></span>{
    <span class="hljs-built_in"><span class="hljs-built_in">console</span></span>.log(<span class="hljs-string"><span class="hljs-string">'app is listening at port 3000'</span></span>);
  });
  </code></pre><p>执行</p><p><code>$ node app.js</code></p><p>这时候我们的 app 就跑起来了，终端中会输出 <code>app is listening at port 3000</code>。这时我们打开浏览器，访问 <code>http://localhost:3000/</code>，会出现 <code>Hello World</code>。如果没有出现的话，肯定是上述哪一步弄错了，自己调试一下。</p><p><img src="http://labfile.oss.aliyuncs.com/courses/493/wm.jpg" alt="此处输入图片的描述"></p><h3 id="labdoc-header-8">2.3 补充知识</h3>
  <p>在这个例子中，node代码监听了3000端口，用户通过访问<code>http://localhost:3000/</code> 得到了内容，为什么呢？</p><h4 id="2-3-1-">2.3.1 端口</h4>
  <p>端口的作用：<code>通过端口来区分出同一电脑内不同应用或者进程，从而实现一条物理网线(通过分组交换技术-比如internet)同时链接多个程序</code>
  <a href="http://en.wikipedia.org/wiki/Port_(computer_networking" target="_blank">Port_(computer_networking)</a>)</p><p>端口号是一个 16位的 uint, 所以其范围为 1 to 65535 (对TCP来说, port 0 被保留，不能被使用. 对于UDP来说, source端的端口号是可选的， 为0时表示无端口).</p><p><code>app.listen(3000)</code>，进程就被打标，电脑接收到的3000端口的网络消息就会被发送给我们启动的这个进程</p><h4 id="2-3-2-url">2.3.2 URL</h4>
  <p><a href="http://www.ietf.org/rfc/rfc1738.txt" target="_blank">RFC1738</a> 定义的url格式笼统版本<code>&lt;scheme&gt;:&lt;scheme-specific-part&gt;</code>，
  scheme有我们很熟悉的<code>http</code>、<code>https</code>、<code>ftp</code>，以及著名的<code>ed2k</code>，<code>thunder</code>。</p><p>通常我们熟悉的url定义成这个样子</p><pre><code class="hljs ruby">&lt;scheme&gt;<span class="hljs-symbol"><span class="hljs-symbol">://&lt;user&gt;</span></span><span class="hljs-symbol"><span class="hljs-symbol">:&lt;password&gt;</span></span>@&lt;host&gt;<span class="hljs-symbol"><span class="hljs-symbol">:&lt;port&gt;/&lt;url-path&gt;</span></span>
  </code></pre><p>用过ftp的估计能体会这么长的，网页上很少带auth信息，所以就精简成这样:</p><pre><code class="hljs ruby">&lt;scheme&gt;<span class="hljs-symbol"><span class="hljs-symbol">://&lt;host&gt;</span></span><span class="hljs-symbol"><span class="hljs-symbol">:&lt;port&gt;/&lt;url-path&gt;</span></span>
  </code></pre><p>在上面的例子中, scheme=http, host=localhost, port=3000, url-path=/, 再联想对照一下浏览器端<code>window.location</code>对象。
  著名的<code>localhost</code>，你可以在电脑的hosts文件上找到</p><p><a href="http://en.wikipedia.org/wiki/URI_scheme" target="_blank">在这篇文章中提到</a>： <code>URI schemes are frequently and incorrectly referred to as "protocols", or specifically as URI protocols or URL protocols, since most were originally designed to be used with a particular protocol, and often have the same name</code>，比较认同这个观点，尤其是今天移动设备的时代里， android和ios的开发中大量使用uri作为跨app通讯通道，把scheme理解为协议略狭隘了。</p><h3 id="labdoc-header-9">2.4 尾声</h3>
  <p>在了解完端口和url之后，再去看例子代码，相信应该好理解很多。
  有必要的话，还可以在解刨一下express的use逻辑，对峙<code>http.createServer</code>，相信还有火花，:)</p><h2 id="labdoc-header-10">三、实验总结</h2>
  <ul>
  <li><code>Node.js</code>以<code>npm</code>作为包管理工具，主要的命令有<code>npm install</code>、<code>npm list</code>等。</li>
  <li>web服务器启动时，会监听一个特定的端口</li>
  <li><code>url</code>和<code>uri</code>常用来定位资源，但要注意不一定只是用来定位网页</li>
  </ul>
  <h2 id="labdoc-header-11">四、课后作业</h2>
  <ul>
  <li>除了<code>install</code>和<code>list</code>，<code>npm</code>还有其他多种命令，请你在控制台输入npm,看看还有哪些命令以及它们的作用</li>
  <li>测试若在一台机器上运行多个Node.js服务器进程且它们监听端口相同时时会发生什么样的情况.</li>
  </ul>
  <h2 id="labdoc-header-12">五、相关链接</h2>
  <ul>
  <li><a href="https://cnodejs.org" target="_blank">CNode社区</a> - Node.js专业中文社区。</li>
  </ul>
  </div>
</body>
</html>